<?php
namespace Infinity;

class FieldManager {

  private $entity_type;
  private $entity;

  /**
   * @param string $entity_type
   * @param object $entity
   */
  public function __construct($entity_type, $entity)
  {
    $this->entity_type = $entity_type;
    $this->entity = $entity;
  }

  /**
   * $field->is('field_name', 'value', 'william', 'like')
   * @param string $field
   * @param string $column
   * @param string $value
   * @param string $operator
   * @return bool
   */
  public function is($field, $column, $value, $operator = '==')
  {
    if($operator == '=') $operator = '==';
    if($operator == '!=') $operator = '<>';
    if($operator == 'IN' && !is_array($value)) $operator = '==';
    if($operator == '==' && is_array($value)) $operator = 'IN';
    if(empty($this->entity->{$field}[LANGUAGE_NONE][0])) {
      watchdog('infinity', $field.' field not set.', WATCHDOG_ERROR);
      return false;
    }
    $fields = field_get_items($this->entity_type, $this->entity, $field);
    foreach ($fields as $v) {
      if($operator == '==' && $v[$column] == $value) return true;
      if($operator == '>' && $v[$column] > $value) return true;
      if($operator == '>=' && $v[$column] >= $value) return true;
      if($operator == '<' && $v[$column] < $value) return true;
      if($operator == '<=' && $v[$column] <= $value) return true;
      if($operator == '<>' && $v[$column] != $value) return true;
      if($operator == 'IN' && in_array($v[$column], $value)) return true;
      if($operator == 'NOT IN' && !in_array($v[$column], $value)) return true;
    }
    return false;
  }

  /**
   * 当前时间是否在日期范围内
   * @param $field
   * @return bool
   */
  public function inDateRange($field)
  {
    if($this->getValue($field, 'date_type') == 'datestamp') {
      $now = time();
      return $this->is($field, 'value', $now, '<=') && $this->is($field, 'value2', $now, '>=');
    }
    return false;
  }

  public function clean($field) {
    unset($this->entity->{$field});
    return $this;
  }

  /**
   * @param string $field
   * @return bool|array
   */
  public function getItem($field, $index=0){
    $fields = field_get_items($this->entity_type, $this->entity, $field);
    if(count($fields))
      return $fields[$index];
    else
      return false;
  }

  /**
   * @param string $field
   * @return array
   */
  public function getItems($field){
    return field_get_items($this->entity_type, $this->entity, $field);
  }

  /**
   * @param string $field
   * @param string $column
   * @param $default_value
   * @return mixed
   */
  public function getValue($field, $column='value', $default_value=null, $index=0){
    $item = $this->getItem($field, $index);
    if(is_array($item) && array_key_exists($column, $item))
      return $item[$column];
    else
      return $default_value;
  }

  /**
   * @param string $field
   * @param string $column
   * @return array
   */
  public function getValues($field, $column='value'){
    $items = $this->getItems($field);
    $results = array();
    if(is_array($items))foreach($items as $item) {
      $results []= $item[$column];
    }
    return $results;
  }

  /**
   * @param string string $field
   * @param string string $column
   * @param $value
   */
  public function setValue($field, $column, $value, $index=0)
  {
    $this->setItem($field, array($column => $value), $index);
    return $this;
  }

  /**
   * @param string $field
   * @param string $column
   * @param array $values
   * @return object
   */
  public function setValues($field, $column, array $values) {
    unset($this->entity->{$field});
    $this->entity->{$field} = array();
    foreach($values as $value) {
      $this->setItem($field, array($column => $value), -1);
    }
    return $this->entity;
  }

  /**
   * @param string $field
   * @param string $item
   * @param int $index
   * @return object
   */
  public function setItem($field, $item, $index=0) {
    if($index >= 0)
      $this->entity->{$field}[LANGUAGE_NONE][$index] = $item;
    else
      $this->entity->{$field}[LANGUAGE_NONE][] = $item;
    return $this->entity;
  }

  public function appendItem($field, $item) {
    $this->setItem($field, $item, -1);
    return $this;
  }

  public function getAllowedValues($field){
    $data = field_info_field($field);
    if(isset($data['settings']['allowed_values']) && is_array($data['settings']['allowed_values']))
      return $data['settings']['allowed_values'];
    return array();
  }

  /**
   * @param string $field
   * @return array
   */
  public function getTerms($field)
  {
    $tids = $this->getValues($field, 'tid');
    if($tids)
      return taxonomy_term_load_multiple($tids);
    else
      return array();
  }

  /**
   * @param $field
   * @return Entity
   */
  public function getTerm($field)
  {
    $tid = $this->getValue($field, 'tid');
    if($tid) {
      return Entity::load('taxonomy_term', $tid);
    }
    else
      return NULL;
  }

  /**
   * @param string $field
   * @param string|array $name
   * @param int $index
   * @return object
   */
  public function setTerm($field, $name, $index=0) {
    if(is_numeric($name)) {
      return $this->setValue($field, 'tid', $name, $index);
    }
    $field_info = field_info_field($field);
    if($field_info['type'] != 'taxonomy_term_reference') return $this;
    $vocabulary = $field_info['settings']['allowed_values'][0]['vocabulary'];
    
    // term names array
    if(is_array($name) && count($name) == 1) $name = array_pop($name);
    if(is_array($name)) {
      $tail = $name[count($name)-1];
      $terms = taxonomy_get_term_by_name($tail, $vocabulary);
      foreach($terms as $term) {
        $tmp_names = array();
        $parents = \Infinity\Term::getParents($term->tid);
        $parents = array_reverse($parents);
        foreach($parents as $parent) {
          $tmp_names[]= $parent->name;
        }
        $return = true;
        for($i=count($name)-1,$i2=1; $i >=0; $i--,$i2++) {
          if($name[$i] != $tmp_names[count($tmp_names)-$i2]) $return = false;
        }
        if($return) {
          return $this->setValue($field, 'tid', $term->tid, $index);
        }
      }
      return $this;
    }

    $terms = taxonomy_get_term_by_name($name, $vocabulary);
    if($terms) {
      $term = array_pop($terms);
      return $this->setValue($field, 'tid', $term->tid, $index);
    }
    return $this;
  }
  
  /**
   * @param string $field
   * @param array $names
   * @return object
   */
  public function setTerms($field, array $names) {
    unset($this->entity->{$field});
    foreach($names as $name) {
      $this->setTerm($field, $name, -1);
    }
    return $this;
  }

  /**
   * @param string $field
   * @param string|array $name
   * @return boolean
   */
  public function checkTermName($field, $name) {
    $field_info = field_info_field($field);
    if($field_info['type'] != 'taxonomy_term_reference') return false;
    $vocabulary = $field_info['settings']['allowed_values'][0]['vocabulary'];
    // term names array
    if(is_array($name) && count($name) == 1) $name = array_pop($name);
    if(is_array($name)) {
      $tail = $name[count($name)-1];
      $terms = taxonomy_get_term_by_name($tail, $vocabulary);
      foreach($terms as $term) {
        $tmp_names = array();
        $parents = \Infinity\Term::getParents($term->tid);
        $parents = array_reverse($parents);
        foreach($parents as $parent) {
          $tmp_names[]= $parent->name;
        }
        $return = true;
        for($i=count($name)-1,$i2=1; $i >=0; $i--,$i2++) {
          if($name[$i] != $tmp_names[count($tmp_names)-$i2]) $return = false;
        }
        if($return) return true;
      }
      return false;
    }

    $terms = taxonomy_get_term_by_name($name, $vocabulary);
    return count($terms) > 0 ? true : false;
  }

  /**
   * @param string $field
   * @param array $names
   * @return boolean
   */
  public function checkTermNames($field, array $names) {
    foreach($names as $name) {
      if(!$this->checkTermName($field, $name))
        return false;
    }
    return true;
  }

  public function setRoles($field, array $names) {
    unset($this->entity->{$field});
    foreach($names as $name) {
      $this->setRole($field, $name, -1);
    }
    return $this;
  }

  /**
   * @param $field
   * @param $name
   * @param $index
   * @return object
   */
  public function setRole($field, $name, $index=0) {
    if(is_numeric($name)) {
      return $this->setValue($field, 'rid', $name, $index);
    }
    $role = user_role_load_by_name(trim($name));
    if($role) {
      return $this->setValue($field, 'rid', $role->rid, $index);
    }
    return $this;
  }

  /**
   * @param $field
   * @return mixed
   */
  public function getFile($field)
  {
    $fid = $this->getValue($field, 'fid');
    $file = NULL;
    if($fid)
      $file = file_load($fid);
    return $file;
  }

  /**
   * @param $field
   * @param $file_path
   * @param int $index
   * @return $this
   */
  public function setFile($field, $file_path, $index=0) {
    $file = (object) array(
      'uid' => $GLOBALS['user']->uid,
      'uri' => $file_path,
      'filemime' => file_get_mimetype($file_path),
      'status' => 1,
      'display' => 1
    );
    $field_info = field_info_instance($this->entity_type, $field, $this->entity->type);
    if(!empty($field_info['settings']['file_directory'])) {
      $file = file_copy($file, $field_info['settings']['file_directory']);
    } else {
      $file = file_copy($file, 'public://');
    }
    $this->setItem($field, (array)$file, $index);
    return $this;
  }

  /**
   * @param $field
   * @param array $file_path_arr
   * @return $this
   */
  public function setFiles($field, array $file_path_arr) {
    foreach($file_path_arr as $file_path) {
      $this->setFile($field, $file_path, -1);
    }
    return $this;
  }

  public function setImageFromRemote($field, $url, $index=0) {
    $file = \Infinity\File::getRemoteImage($url, 'public://remote/');
    $file = file_save($file);
    $this->setItem($field, (array)$file, $index);
    return $this;
  }

  /**
   * @param $field
   * @return bool|FieldManager
   */
  public function getFieldCollection($field)
  {
    if(!module_exists('field_collection')) return false;
    $field_collection_item_id = $this->getValue($field, 'value');
    $entity = field_collection_item_load($field_collection_item_id);
    return new FieldManager('field_collection_item', $entity);
  }

  /**
   * @param $field
   * @return array(FieldManager)|bool
   */
  public function getFieldCollections($field)
  {
    if(!module_exists('field_collection')) return false;
    $field_collection_item_ids = $this->getValues($field, 'value');
    $arr = array();
    foreach($field_collection_item_ids as $id) {
      $entity = field_collection_item_load($id);
      if($entity)
        $arr []= new FieldManager('field_collection_item', $entity);
    }
    return $arr;
  }

  /**
   * @param string $field
   * @param string $glue
   * @return string
   */
  public function getTermsName($field, $glue=', ')
  {
    $terms = $this->getTerms($field);
    $names = array();
    foreach($terms as $term) {
      $names[]= $term->name;
    }
    return implode($glue, $names);
  }

  public function save()
  {
    if($this->entity_type == 'node')
      node_save($this->entity);
    elseif($this->entity_type == 'user')
      user_save($this->entity);
    elseif($this->entity_type == 'taxonomy_term')
      taxonomy_term_save($this->entity);
    else
      entity_get_controller($this->entity_type)->save($this->entity);
    return $this->entity;
  }

  public function count($field) {
    return count(field_get_items($this->entity_type, $this->entity, $field));
  }

  /**
   * @param $field
   * @param $variables array('style_name' => 'thumbnail', 'alt' => '', 'title' => '');
   * @param int $index
   * @return string
   */
  public function renderImage($field, $variables, $index=0) {
    $field_info = field_info_instance($this->entity_type, $field, $this->entity->type);
    $default_uri = '';
    if(!empty($field_info['settings']['default_image'])) {
      $file = file_load($field_info['settings']['default_image']);
      $default_uri = $file->uri;
    }
    $uri = $this->getValue($field, 'uri', $default_uri, $index);
    if($uri) {
      $variables['path'] = $uri;
      return theme('image_style', $variables);
    }
    return '';
  }

  public function getAllFieldNames() {
    return array_keys(field_info_instances($this->entity_type, $this->entity->type));
  }
}